package ptij.ADSL.pn.ATUC;

import ptolemy.kernel.*;
import ptolemy.kernel.util.*;
import ptolemy.data.*;
import ptolemy.data.expr.*;
import ptolemy.actor.*;

import ptij.ADSL.common.MuxDataFrame;

public class CRCTx extends TypedAtomicActor {

    /* input Ports */
    public TypedIOPort input;
    
    /* output Ports */
    public TypedIOPort output;

    public Parameter AS0Bytes;
    public Parameter LS0Bytes;
    
    public Parameter AS0Map;
    public Parameter LS0Map;
    
    public Parameter S;

    public CRCTx(TypedCompositeActor container, String name) throws
        NameDuplicationException, IllegalActionException {
        super(container, name);
	
	input = new TypedIOPort(this, "input", true, false);
	input.setTypeEquals(ObjectToken.class);
	
	output = new TypedIOPort(this, "output", false, true);
	output.setTypeEquals(ObjectToken.class);

	AS0Bytes = new Parameter(this, "AS0Bytes", new IntToken(8));
	LS0Bytes = new Parameter(this, "LS0Bytes", new IntToken(8));
	
	AS0Map = new Parameter(this, "AS0Map", new BooleanToken(false));
	LS0Map = new Parameter(this, "LS0Map", new BooleanToken(false));
	
	S = new Parameter(this, "S", new IntToken(1));
    }

    public void fire() throws IllegalActionException {
    
        ObjectToken ot = (ObjectToken) input.get(0);
	MuxDataFrame mf = (MuxDataFrame) ot.getValue();
	
	if(_frameCounter == 0) {
            if(_iteration == 0) {
	        _fastCRC(mf, false);
	        _iteration = 1;
	    }
	    else {
	        _CRC(0, _fastReg);
                mf.setFastByte(_fastReg);
	        _fastReg = 0;
	        _fastCRC(mf, true);
	    }
	}		
	else
	if(_frameCounter == 1) {
            if(_iteration == 1) {
	        _interCRC(mf, false);
	        _iteration = 2;
	    }
	    else {
	        _CRC(0, _interReg);
	        mf.setFastByte(_interReg);
	        _interReg = 0;
	        _interCRC(mf, true);
	    }
	}
	else {
	    if(_frameCounter % (_S + 1) == 0) {
	        _fastCRC(mf, true);
	    }
	    else {
	        _interCRC(mf, true);
	    }
	}

	_frameCounter++;
	if(_frameCounter == ATUC.framesPerSuperframe + ATUC.framesPerSuperframe*_S) 
	    _frameCounter = 0;
	
	ot = new ObjectToken(mf);
	output.broadcast(ot);
    }
    
    public void initialize() throws IllegalActionException {
        super.initialize();

	IntToken it = (IntToken) AS0Bytes.getToken();
	_AS0Bytes = it.intValue();
	
	it = (IntToken) LS0Bytes.getToken();
	_LS0Bytes = it.intValue();
	
	BooleanToken _bt = (BooleanToken) AS0Map.getToken();
	_AS0Map = _bt.booleanValue();
	
	_bt = (BooleanToken) LS0Map.getToken();
	_LS0Map = _bt.booleanValue();
	
	it = (IntToken) S.getToken();
	_S = it.intValue(); 
	
	_iteration = 0;

        _frameCounter = 0;
	_interReg = 0;	
	_fastReg = 0;
	
	_checkParameters();
    }
    
    private void _fastCRC(MuxDataFrame mf, boolean first) {
        if(first) {
	    _fastReg = _CRC(mf.getFastByte(), _fastReg);
	}
	
	if(!_AS0Map) {
	    for(int i = 0;i < _AS0Bytes;i++)
	        _fastReg = _CRC(mf.getAS0Byte(i), _fastReg);
	}
	
	if(!_LS0Map) {
	    for(int i = 0;i < _LS0Bytes;i++)
	        _fastReg = _CRC(mf.getLS0Byte(i), _fastReg);
	}
	
	if(mf.hasAEX()) {
	    _fastReg = _CRC(mf.getAEXByte(), _fastReg);
	}
	
	if(mf.hasLEX()) {
	    _fastReg = _CRC(mf.getLEXByte(), _fastReg);
	}
    }
    
    private void _interCRC(MuxDataFrame mf, boolean first) {
        if(first) {
	    _interReg = _CRC(mf.getFastByte(), _interReg);	
	}
	
	if(_AS0Map) {
	    for(int i = 0;i < _AS0Bytes;i++)
	        _interReg = _CRC(mf.getAS0Byte(i), _interReg);
	}
	
	if(_LS0Map) {
	    for(int i = 0;i < _LS0Bytes;i++)
	        _interReg = _CRC(mf.getLS0Byte(i), _interReg);
	}
	
	if(mf.hasAEX()) {
	    _interReg = _CRC(mf.getAEXByte(), _interReg);
	}
	
	if(mf.hasLEX()) {
	    _interReg = _CRC(mf.getLEXByte(), _interReg);
	}
    }
    
    private int _CRC(int b, int reg) {
        int out;
        for(int i = 0;i < 8;i++) {
	    out = reg & 0x80;
	    reg = (reg << 1) | ((b >> i) & 0x01);
	    if(out != 0) reg = reg ^ _poly;
	}	
	return reg;
    }

    private void _checkParameters() throws IllegalActionException {
    
        if(_AS0Bytes < 0) {
	    throw new IllegalActionException(this, "AS0 Bytes < 0");	
	}
	
	if(_LS0Bytes < 0) {
	    throw new IllegalActionException(this, "LS0 Bytes < 0");
	}
	
	if(_S != 1 && _S != 2 && _S != 4 && _S != 8 && _S != 16) {
	    throw new IllegalActionException(this, "\nInvalid value(" +
	        _S + ") for parameter S");
	}	
    }
        
    private int _frameCounter;
    private int _fastReg;
    private int _interReg;
    private final static int _poly = 0x1d;
    private byte _iteration;

    private int _AS0Bytes;
    private int _LS0Bytes;
    private boolean _AS0Map;
    private boolean _LS0Map;
    private int _S;
}
